{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a57e52e5-ca2b-44bd-8284-d92427489293",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: trapped.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57106fea-2057-4d39-a359-26e3ba067979",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "import numpy as np\n",
    "import sdf\n",
    "from frontend import App\n",
    "\n",
    "# create an app\n",
    "app = App.create(\"trapped\")\n",
    "filepath = \"/tmp/squishy.tmp.ply\"\n",
    "\n",
    "# create squishy ball mesh with spiky protrusions if not exists\n",
    "if not os.path.exists(filepath):\n",
    "    V, F = app.mesh.icosphere(r=2, subdiv_count=2)\n",
    "    func = sdf.sphere(1.1)\n",
    "    # add capsule spikes radiating outward from center\n",
    "    for f in F:\n",
    "        d = np.mean(V[f], axis=0)\n",
    "        if d[0] > 0:\n",
    "            func = func | sdf.capsule(-d, d, 0.05)\n",
    "    func.save(filepath, step=0.03)\n",
    "\n",
    "# load and process the squishy mesh\n",
    "V, F, T = (\n",
    "    app.mesh.load_tri(filepath)\n",
    "    .decimate(100000)\n",
    "    .tetrahedralize()\n",
    "    .normalize()\n",
    "    .scale(0.97)\n",
    ")\n",
    "app.asset.add.tet(\"squishy\", V, F, T)\n",
    "\n",
    "# create a scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# add invisible sphere container with narrow passages\n",
    "(\n",
    "    scene.add.invisible.sphere([0, 0, 0], 0.98)\n",
    "    .invert()\n",
    "    .radius(0.4, 2)\n",
    "    .radius(0.4, 3)\n",
    "    .radius(10, 4)\n",
    ")\n",
    "\n",
    "# add two squishy balls positioned to collide\n",
    "scene.add(\"squishy\").at(0.5, 0, 0).jitter()\n",
    "scene.add(\"squishy\").at(-0.5, 0, 0).jitter()\n",
    "\n",
    "# compile the scene and report stats\n",
    "scene = scene.build().report()\n",
    "\n",
    "# preview the initial scene\n",
    "scene.preview()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0fb12c0-69ba-4201-9018-4a098c428a9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a new session with the compiled scene\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# set session parameters - disable gravity and increase sparse matrix capacity\n",
    "(\n",
    "    session.param.set(\"gravity\", 0.0)\n",
    "    .set(\"csrmat-max-nnz\", 3000000)\n",
    "    .set(\"dt\", 0.01)\n",
    ")\n",
    "\n",
    "# set dynamic playback speed to slow down after initial collision\n",
    "session.param.dyn(\"playback\").time(2.99).hold().time(3).change(0.1)\n",
    "\n",
    "# build this session\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b32dd182-3bbc-44e8-9bfd-aa0ad70fe2f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# start the simulation and live-preview the results\n",
    "session.start().preview()\n",
    "\n",
    "# also show simulation logs in realtime\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "318a7f9c-efb6-400f-8a13-25a429e217cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an animation from the simulation results\n",
    "session.animate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f3bcfda-4a38-4e0f-b0f0-e35d232fca8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# export the animation to file\n",
    "session.export.animation()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fdeab632-6f3d-428f-8057-8b796b3ce6a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
